package org.wevil.util;

import cn.hutool.core.lang.id.NanoId;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 随机数工具
 */
public class RandomUtil {

    // -------------------------------------随机数参数-------------------------------------------------------//

    /** 字符集，包括0-9的数字和A-Z的字母 */
    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    /**
     * 地区码
     * */
    private static final String[] AREA_CODES = {"110000", "120000", "130000", "140000", "150000", "210000", "220000", "230000", "310000", "320000", "330000", "340000", "350000", "360000", "370000", "410000", "420000", "430000", "440000", "450000", "460000", "500000", "510000", "520000", "530000", "540000", "610000", "620000", "630000", "640000", "650000", "710000"};
    /**
     * 校验码权重
     * */
    private static final int[] WEIGHTS = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    /**
     * 校验字符
     * */
    private static final char[] CHECK_CODES = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

    /**
     * 常见的中文姓氏
     * */
    private static final String[] SURNAMES = {"赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许",
            "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范",
            "彭", "郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "俞", "任", "袁", "柳", "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", "倪", "汤",
            "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", "元", "卜", "顾", "孟", "平", "黄", "和", "穆",
            "萧", "尹"};

    /**
     * 常见的中文名字用字
     * */
    private static final String[] FIRST_NAMES = {"伟", "刚", "勇", "毅", "俊", "峰", "强", "军", "平", "保", "东", "文", "辉", "力", "明", "永", "健", "世", "广", "志", "义",
            "兴", "良", "海", "山", "仁", "波", "宁", "贵", "福", "生", "龙", "元", "全", "国", "胜", "学", "祥", "才", "发", "武", "新", "利", "清", "飞", "彪", "宏", "德",
            "光", "天", "达", "安", "岩", "中", "茂", "进", "林", "有", "坚", "和", "彬", "柏", "义", "兴", "良", "海", "山", "仁", "波", "宁", "贵", "福", "生", "龙", "元",
            "全", "国", "胜", "学", "祥", "才", "发", "武", "新", "利", "清", "飞", "彪", "宏", "德", "光", "天", "达", "安", "岩", "茂", "进", "林", "坚", "和", "彬", "柏",
            "曦", "景", "岚", "婧", "璇", "萱", "轩", "怡", "婷", "颖", "珊", "妮", "雯", "媛", "敏", "佳", "莲", "彤", "妍", "怡", "佩", "淇", "雨", "娜", "莹", "悦", "彤",
            "秀", "娟", "英", "华", "慧", "巧", "美", "静", "淑", "惠", "珠", "翠", "雅", "芝", "玉", "萍", "红", "娥", "玲", "芬", "芳", "燕", "彩", "春", "菊", "勤", "珍",
            "贞", "莉", "兰", "凤", "洁", "梅", "琳", "素", "云", "莲", "真", "环", "雪", "荣", "爱", "妹", "霞", "香", "月", "莺", "媛", "艳", "瑞", "凡", "佳", "嘉", "琼",
            "勤", "珍", "贞", "莉", "桂", "娣", "叶", "璧", "璐", "娅", "琦", "晶", "妍", "茜", "秋", "珊", "莎", "锦", "黛", "青", "倩", "婷", "姣", "婉", "娴", "瑾", "颖",
            "露", "瑶", "怡", "婵", "雁", "蓓", "纨", "仪", "荷", "丹", "蓉", "眉", "君", "琴", "蕊", "薇", "菁", "梦", "岚", "苑", "婕", "馨", "瑗", "琰", "韵", "融", "园", "艺",
            "咏", "卿", "聪", "澜", "纯", "毓", "悦", "昭", "冰", "爽", "琛", "茗", "羽", "希", "宁", "欣", "飘", "育", "滢", "馥", "筠", "柔", "竹", "霭", "凝", "晓", "欢", "霄",
            "枫", "芸", "菲", "寒", "伊", "亚", "宜", "可", "姬", "舒", "影", "荔", "枝", "思", "丽"};

    /**
     *  常见的手机号前缀
     * */
    private static final String[] PREFIXES = {
            "130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
            "145", "147", "150", "151", "152", "153", "155", "156", "157", "158", "159",
            "165", "166", "167",
            "170", "171", "173", "175", "176", "177", "178",
            "180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
            "198"
    };

    // -----------------------------------常用表单提交参数--------------------------------------------------------//

    /**
     * 生成一个随机的主键。20位
     * */
    public static String id(){
        return NanoId.randomNanoId(20);
    }

    /**
     * 生成一个随机的主键
     * @param length 主键长度
     * */
    public static String id(int length){
        return NanoId.randomNanoId(length);
    }

    /**
     * 生成一个随机的纳税人识别号
     *
     * @return 15位字符的随机纳税人识别号
     */
    public static String tax() {
        StringBuilder tin = new StringBuilder(15);
        for (int i = 0; i < 15; i++) {
            int index = new SecureRandom().nextInt(CHARACTERS.length());
            tin.append(CHARACTERS.charAt(index));
        }
        return tin.toString();
    }

    /**
     * 随机生成一个身份证号码
     * */
    public static String idcard() {
        Random random = new Random();
        // 随机选择一个地区码
        String areaCode = AREA_CODES[random.nextInt(AREA_CODES.length)];
        // 随机生成一个出生日期（这里简单生成一个合法的日期范围）
        Calendar calendar = Calendar.getInstance();
        calendar.set(1950 + random.nextInt(70), random.nextInt(12), random.nextInt(28) + 1); // 年份范围：1950-2020，注意月份从0开始，日期从1开始
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        String birthDate = sdf.format(calendar.getTime());
        // 随机生成顺序码
        String orderCode = String.format("%03d", random.nextInt(1000));
        // 计算校验码
        String base = areaCode + birthDate + orderCode;
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum += Character.getNumericValue(base.charAt(i)) * WEIGHTS[i];
        }
        char checkCode = CHECK_CODES[sum % 11];
        // 拼接完整的身份证号码
        return areaCode + birthDate + orderCode + checkCode;
    }

    /**
     * 随机生成一个中文姓名
     * */
    public static String name() {
        Random random = new Random();
        // 随机选择一个姓氏
        String surname = SURNAMES[random.nextInt(SURNAMES.length)];
        // 随机选择两个名字用字作为名字
        String firstName1 = FIRST_NAMES[random.nextInt(FIRST_NAMES.length)];
        String firstName2 = FIRST_NAMES[random.nextInt(FIRST_NAMES.length)];
        // 拼接完整的中文姓名
        return surname + firstName1 + firstName2;
    }

    /**
     * 随机生成一个手机号
     * */
    public static String phone() {
        Random random = new Random();
        // 随机选择一个前缀
        String prefix = PREFIXES[random.nextInt(PREFIXES.length)];
        // 生成8位随机数作为后缀
        StringBuilder suffix = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            suffix.append(random.nextInt(10));
        }
        // 拼接前缀和后缀形成完整的手机号
        return prefix + suffix.toString();
    }

    /**
     * 生成随机坐标，可配置最大值和最小值
     *
     * @param minLongitude 最小经度
     * @param maxLongitude 最大经度
     * @param minLatitude  最小纬度
     * @param maxLatitude  最大纬度
     * @return 经纬度用逗号分割，示例：41.532079,86.306517
     */
    public static String randomCoordinate(
            Double minLongitude, Double maxLongitude,
            Double minLatitude, Double maxLatitude) {
        // 拼接经纬度
        Double longitude = RandomUtil.randomDouble(minLongitude, maxLongitude,6);
        Double latitude = RandomUtil.randomDouble(minLatitude, maxLatitude,6);
        return latitude + "," + longitude;
    }

    // -----------------------------------随机数--------------------------------------------------------//

    /**
     * 生成指定范围内的随机整数（包括边界值）
     * */
    public static Integer randomInt(int min, int max) {
        if (min > max) {
            throw new IllegalArgumentException("最小值不能大于最大值");
        }
        Random random = new Random();
        // 生成 [0, (max - min + 1)) 范围内的随机整数
        int randomValue = random.nextInt((max - min) + 1);
        // 将生成的随机整数转换为 [min, max] 范围内的值
        return min + randomValue;
    }

    /**
     * 生成一组不重复的随机整数
     *
     * @param num 生成多少个随机数
     * */
    public static List<Integer> randomIntNoRepeat(int min, int max, int num){
        List<Integer> randomList = new ArrayList<>();
        while (randomList.size() < num) {
            Random random = new Random();
            int randomNum = random.nextInt(max) % (max - min + 1) + min;
            if(!randomList.contains(randomNum)){
                randomList.add(randomNum);
            }
        }
        return randomList;
    }

    /**
     * 生成一个随机带整数位的小数，指定最大值和最小值范围。保留2位小数
     * */
    public static Double randomDouble(Double min, Double max){
        return randomDouble(min, max, 2);
    }

    /**
     * 生成一个随机带整数位的小数，指定最大值和最小值范围
     * @param scale 小数位数
     * */
    public static Double randomDouble(Double min, Double max, Integer scale){
        if (min > max) {
            throw new IllegalArgumentException("最小值不能大于最大值");
        }
        if (scale < 0) {
            throw new IllegalArgumentException("小数位数不能小于0");
        }

        Random random = new Random();
        // 生成 [0.0, 1.0) 范围内的随机小数
        double randomDouble = random.nextDouble();
        // 将生成的随机小数转换为 [min, max] 范围内的值
        double randomValue = min + (max - min) * randomDouble;

        // 使用 BigDecimal 对结果进行四舍五入到指定的小数位数
        BigDecimal bd = new BigDecimal(Double.toString(randomValue));
        bd = bd.setScale(scale, RoundingMode.HALF_UP);
        return bd.doubleValue();
    }

    // -----------------------------------随机日期--------------------------------------------------------//

    /**
     * 根据年份和月份，生成随机的连续天数
     *
     * @param days 连续的天数
     * @return 示例：[19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
     * */
    public static List<Integer> randomSeriesDay(Integer year, Integer month, Integer days){
        // 获取当月的总天数
        Integer days_of_months = DateUtil.getDays(year, month);
        // 起始数
        Integer start = randomInt(1, days_of_months + 2 - days);
        List<Integer> nums = new ArrayList<>();
        for(int i=1; i<=days; i++){
            nums.add(start);
            start++;
        }
        return nums;
    }

    // -----------------------------------特殊随机--------------------------------------------------------//

    /**
     * 红包根据总金额和指定个数随机生成金额
     *
     * @param total 总金额
     * @param splitCount 个数
     * @param min 最小金额
     * @param max 最大金额
     */
    public static List<Integer> splitRedPacket(int total, int splitCount, int min, int max) {
        System.out.println("总金额：	" + total);
        System.out.println("个数：	" + splitCount);
        System.out.println("最小金额：	" + min);
        System.out.println("最大金额：	" + max);

        if(max * splitCount < total || splitCount * min > total){
            System.out.println("最小金额太大或者最大金额太小，无法生成随机数");
            return null;
        }

        ArrayList<Integer> al = new ArrayList<Integer>();
        Random random = new Random();

        if ((splitCount & 1) == 1) {// 奇数个红包，需要单独将其中一个红包先生成，以保证后续算法拆分份数为偶数。
            System.out.println("红包个数" + splitCount + "是奇数，单独生成一个红包");
            int num = 0;
            do {
                num = random.nextInt(max);
                // num = (total - num) % (splitCount / 2) + num; //
                // 将后面算法拆分时的余数加入到这个随机值中。
                System.out.println("单个的随机红包为：" + num);
            } while (num >= max || num <= min);

            total = total - num;
            al.add(num);
        }
        int couples = splitCount >> 1;
        int perCoupleSum = total / couples;

        if ((splitCount & 1) == 1) {
            System.out.println("处理后剩余的金额为：" + total);
        }
        System.out.println("将" + total + "元拆分为" + couples + "对金额，每对总额：" + perCoupleSum);

        for (int i = 0; i < couples; i++) {
            Boolean finish = true;
            int num1 = 0;
            int num2 = 0;
            do {
                num1 = random.nextInt(max);
                num2 = perCoupleSum - num1;
                if (!al.contains(num1) && !al.contains(num2)) {
                    if (i == 0) {
                        num1 = (total - couples * perCoupleSum) + num1;
                    }
                }
            } while (num1 < min || num1 > max || num2 < min || num2 > max);
            al.add(num1);
            al.add(num2);
        }
        int check_num = 0;
        System.out.println(Arrays.toString(al.toArray()));

        for (int x : al) {
            check_num = check_num + x;
        }
        System.out.println("验证总和：" + check_num);
        return al;
    }

    public static void main(String[] args) {
        for (int i=0; i<217; i++){
            System.out.println(id(20));
        }
    }
}
